home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Mac-Source 1994 July
/
Mac-Source_July_1994.iso
/
C and C++
/
Libraries
/
CDragAcrossTable 1.0b1
/
CDragAcrossTable.c
next >
Wrap
Text File
|
1993-11-04
|
17KB
|
684 lines
/******************************************************************************
CDragAcrossTable.c
This is an examples class that works with CDragAcrossTask to drag the
selected cells to another postion within the current table or another
CDragAcrossTable in the applicaiton.
This is a subclass of CTable, but it can be made a subclass of any CTable
subclass, such as CArrayPane. Too bad I can't/don't use
multiple-inheritance.
AUTHOR: Andrew_Gilmartin@Brown.Edu
MODIFIED: 93-11-04
Copyright (C) 1993 by Brown University. All rights reserved.
Permission is granted to any individual or institution to use, copy,
or redistribute the binary version of this software and its
documentation provided this notice and the copyright notices are
retained. Permission is granted to any individual or non-profit
institution to use, copy, modify, or redistribute the source files
of this software provided this notice and the copyright notices are
retained. This software may not be distributed for profit, either
in original form or in derivative works, nor can the source be
distributed to other than an individual or a non-profit institution.
Any individual or group interested in seeing and/or using these
source files but who are prevented from doing so by the above
constraints should contact Don Wolfe, Vice-President for Computer
Systems at Brown University, (401) 863-7247, for possible
software licensing of the source developed at Brown.
Brown University and Andrew James Gilmartin make no representations
about the suitability of this software for any purpose.
BROWN UNIVERSITY AND ANDREW JAMES GILMARTIN GIVE NO WARRANTY, EITHER
EXPRESS OR IMPLIED, FOR THE PROGRAM AND/OR DOCUMENTATION PROVIDED,
INCLUDING, WITHOUT LIMITATION, WARRANTY OF MERCHANTABILITY AND
WARRANTY OF FITNESS FOR A PARTICULAR PURPOSE.
******************************************************************************/
#include "ForgetRgn.h"
#include "CDragAcrossTask.h"
#include "CDragAcrossTable.h"
#define IS_EVEN( _n ) \
( ( (_n) & 0x01 ) == 0 )
enum { kBoundarySize = 4 }; // used by DragRowBoundary() & DrawColBoundary()
enum { kAutoScrollSlop = 10 }; // used by ShouldScroll()
Boundary kEmptyBoundary = { -1, -1 };
/******************************************************************************
IDragAcrossTable
Initilaize the drag across table.
******************************************************************************/
void CDragAcrossTable::IDragAcrossTable
( CView* anEnclosure
, CBureaucrat* aSupervisor
, short aWidth
, short aHeight
, short aHEncl
, short aVEncl
, SizingOption aHSizing
, SizingOption aVSizing )
{
ITable
( anEnclosure
, aSupervisor
, aWidth
, aHeight
, aHEncl
, aVEncl
, aHSizing
, aVSizing );
itsSelectedBoundaries = kEmptyBoundary;
} /* IDragAcrossTable */
/******************************************************************************
MoveSelection
This method is called by CDragAcrossTask should the user select a boundary
to move the selection to.
******************************************************************************/
void CDragAcrossTable::MoveSelection( CDragAcrossTable* toTable, Boundary toBoundary )
{
/* nop */
} /* MoveSelection */
/******************************************************************************
DoClick
Did the user click to drag or click to select? Act approp.
******************************************************************************/
void CDragAcrossTable::DoClick( Point hitPt, short modifierKeys, long when )
{
CDragAcrossTask* dragTask;
Cell hitCell;
LongPt pt;
Point startPt;
QDToFrame( hitPt, &pt );
if ( PtInLongRect( &pt, &bounds ) )
{
if ( HitDragZone( hitPt, modifierKeys ) )
{
dragTask = MakeGlobalMouseTask();
FrameToGlobal( &pt, &startPt );
TrackGlobalMouse( dragTask, startPt );
ForgetObject( dragTask );
}
else
{
inherited::DoClick( hitPt, modifierKeys, when );
}
}
else
{
ClickOutsideBounds( hitPt, modifierKeys, when);
}
} /* DoClick */
/******************************************************************************
HitDragZone
Called by DoClick() to determine if the user has clicked in the "drag
zone."
******************************************************************************/
Boolean CDragAcrossTable::HitDragZone( Point hitPt, short modifierKeys )
{
return ( modifierKeys & controlKey ) != 0;
} /* HitDragZone */
/******************************************************************************
MakeGlobalMouseTask
Create the global mouse task. NOTE: aNameIndex of 130 is the TCL default.
******************************************************************************/
CDragAcrossTask* CDragAcrossTable::MakeGlobalMouseTask( void )
{
CDragAcrossTask* dragTask = NULL;
TRY
{
dragTask = new CDragAcrossTask;
dragTask->IDragAcrossTask( this, GetSelectionRgn(), 130 );
}
CATCH
{
ForgetObject( dragTask );
}
ENDTRY
return dragTask;
} /* MakeGlobalMouseTask */
/******************************************************************************
Draw
Draw the content plus the selected boundaries. Assume the pane is
already prepared.
******************************************************************************/
void CDragAcrossTable::Draw( Rect *area )
{
inherited::Draw( area );
/* Update the boundary selection */
if ( ! EqualPt( itsSelectedBoundaries, kEmptyBoundary ) )
{
DrawColBoundary( itsSelectedBoundaries );
DrawRowBoundary( itsSelectedBoundaries );
}
} /* Draw */
/******************************************************************************
DrawRowBoundary
Hilight the row boundary. Assumes the pane is already prepared.
******************************************************************************/
void CDragAcrossTable::DrawRowBoundary( Boundary boundaries )
{
LongRect pixels;
Rect visPixels;
short col;
short row;
/* Make sure there is a row boundary */
if ( boundaries.v == kEmptyBoundary.v )
return;
/* Get boundary's horizontal position and length */
col = boundaries.h / 2;
pixels.left = GetColStart( col );
pixels.right = pixels.left + GetColWidth( col );
pixels.left += kBoundarySize / 2,
pixels.right -= kBoundarySize / 2; // HACK: Makes it look a little better
/* Get boundary's vertical position and height */
row = boundaries.v / 2;
if ( IS_EVEN( boundaries.v ) )
{
// Boundary at the top of the cell
pixels.top = GetRowStart( row ) - kBoundarySize / 2;
pixels.bottom = pixels.top + kBoundarySize;
}
else
{
// Boundary at the bottom of the cell
pixels.bottom
= GetRowStart( row )
+ GetRowHeight( row )
+ kBoundarySize / 2;
pixels.top = pixels.bottom - kBoundarySize;
}
/* Draw boundary rect */
if ( SectAperture( &pixels, &visPixels ) )
{
SetHiliteMode(); // Invert with user's hilite color
InvertRect( &visPixels );
}
} /* DrawRowBoundary */
/******************************************************************************
DrawColBoundary
Hilight the column boundary. Assumes the pane is already prepared.
******************************************************************************/
void CDragAcrossTable::DrawColBoundary( Boundary boundaries )
{
LongRect pixels;
Rect visPixels;
short col;
short row;
/* Make sure there is a column boundary */
if ( boundaries.h == kEmptyBoundary.h )
return;
/* Get boundary's vertical position and height */
row = boundaries.v / 2;
pixels.top = GetRowStart( row );
pixels.bottom = pixels.top + GetRowHeight( row );
pixels.top -= kBoundarySize / 2,
pixels.bottom += kBoundarySize / 2; // HACK: Makes it look a little better
/* Get boundary's horizontal position and length */
col = boundaries.h / 2;
if ( IS_EVEN( boundaries.h ) )
{
// Boundary to the left of the column
pixels.left = GetColStart( col ) - kBoundarySize / 2;
pixels.right = pixels.left + kBoundarySize;
}
else
{
// Boundary to the right of the column
pixels.right
= GetColStart( col )
+ GetColWidth( col )
+ kBoundarySize / 2;
pixels.left = pixels.right - kBoundarySize;
}
/* Draw boundary rect */
if ( SectAperture( &pixels, &visPixels ) )
{
SetHiliteMode(); // Invert with user's hilite color
InvertRect( &visPixels );
}
} /* DrawColBoundary */
/******************************************************************************
SelectBoundaries
Select the cell boundaries. Assumes the pane is already prepared.
******************************************************************************/
void CDragAcrossTable::SelectBoundaries( Boundary boundaries )
{
if ( ! EqualPt( itsSelectedBoundaries, boundaries ) )
{
/*If necessary deselect/draw the previous boundaries */
if ( ! EqualPt( itsSelectedBoundaries, kEmptyBoundary ) )
{
DrawColBoundary( itsSelectedBoundaries );
DrawRowBoundary( itsSelectedBoundaries );
}
itsSelectedBoundaries = boundaries;
/* select/draw new boundaries */
DrawColBoundary( itsSelectedBoundaries );
DrawRowBoundary( itsSelectedBoundaries );
}
} /* SelectBoundaries */
/******************************************************************************
FindBoundaries
Find the boundaries what were hit.
******************************************************************************/
void CDragAcrossTable::FindBoundaries( LongPt* hitPt, Boundary* hitBoundary )
{
hitBoundary->v = FindRowBoundary( hitPt->v );
hitBoundary->h = FindColBoundary( hitPt->h );
} /* FindBoundaries */
/******************************************************************************
FindRowBoundary
Return the row boundary index for the given vertical offset. The
boundary index indicates both the row and the side of the row. To
determine the row divide the index by two. To determine which side
test the index for even-ness; an even index means the boundary is at the
top and an odd index means at the bottom.
******************************************************************************/
short CDragAcrossTable::FindRowBoundary( long vLoc )
{
short row;
long middle;
if ( vLoc >= bounds.bottom )
{
row = ( tableBounds.bottom * 2 ) + 1;
}
else
{
/* Find the row the vLoc is in. */
vLoc -= topLeftIndent.v;
row = itsRows->FindSum( vLoc ) - 1;
if ( row < 0 )
row = 0;
/* Is the boundary to the top or bottom. Which side of the middle? */
middle = GetRowStart( row ) + GetRowHeight( row ) / 2;
if ( vLoc < middle )
row = row * 2; // top
else
row = row * 2 + 1; // bottom
}
return row;
} /* FindRowBoundary */
/******************************************************************************
FindColBoundary
Return the column boundary index for the given horizontal offset. The
boundary index indicates both the column and the side of the column. To
determine the column divide the index by two. To determine which side
test the index for even-ness; an even index means the boundary is to the
left and an odd index means to the left.
******************************************************************************/
short CDragAcrossTable::FindColBoundary( long hLoc )
{
short col;
long middle;
if ( hLoc >= bounds.right )
{
col = ( tableBounds.right * 2 ) + 1;
}
else
{
/* Find the column the hLoc is in. */
hLoc -= topLeftIndent.h;
col = itsCols->FindSum( hLoc ) - 1;
if ( col < 0 )
col = 0;
/* Is the boundary to the left or right. Which side of the middle? */
middle = GetColStart( col ) + GetColWidth( col ) / 2;
if ( hLoc < middle )
col = col * 2; // left
else
col = col * 2 + 1; // right
}
return col;
} /* FindColBoundary */
/******************************************************************************
GetSelectionRgn
Return a region that represents the selection. This methed is used by
CDragAcrossTask.
******************************************************************************/
RgnHandle CDragAcrossTable::GetSelectionRgn( void )
{
RgnHandle cellRgn = NULL;
RgnHandle selectionRgn = NULL;
LongRect cellRect;
Rect cellQDRect;
Cell cell;
TRY
{
cellRgn = NewRgn();
selectionRgn = NewRgn();
Prepare();
/* Brute force iteration over the whole table. I can't seem to figure
out how to get GetSelect() to work. */
for ( cell.h = tableBounds.left; cell.h < tableBounds.right + 1; cell.h++ )
{
for ( cell.v = tableBounds.top; cell.v < tableBounds.bottom + 1; cell.v++ )
{
if ( IsSelected( cell ) )
{
/* Make cell region */
GetCellRect( cell, &cellRect );
InsetLongRect( &cellRect, 1, 1 );
FrameToGlobalR( &cellRect, &cellQDRect );
RectRgn( cellRgn, &cellQDRect );
/* Add cell region to selection region */
UnionRgn( cellRgn, selectionRgn, selectionRgn );
}
}
}
DisposeRgn( cellRgn );
}
CATCH
{
ForgetRgn( cellRgn );
ForgetRgn( selectionRgn );
}
ENDTRY
return selectionRgn;
} /* GetSelectionRgn */
/******************************************************************************
AutoScroll
Scroll the table if necessary. See ShouldScroll() for more information
about scrolling with CDragAcrossTable.
******************************************************************************/
Boolean CDragAcrossTable::AutoScroll( LongPt* mouseLoc )
{
Point theDelta;
if ( ShouldScroll( mouseLoc, &theDelta ) )
{
Scroll( theDelta.h, theDelta.v, TRUE );
if ( itsScrollPane != NULL )
{
itsScrollPane->Calibrate();
}
Prepare();
return TRUE;
}
else
{
return FALSE;
}
} /* AutoScroll */
/******************************************************************************
ShouldScroll
Determine whether the table should scroll given the current mouse location.
Return true or false approp. plus the scrolling delta. (Code taken from
CPanoram::AutoScroll().)
To aid scrolling when the mouse is within the table, this override checks
if the mouseLoc is near the edge of the frame. Near the edge is defined
as kAutoScrollSlop. The reason for scrolling when the mouse is in the
table is that when CDragAcrossTask has content the current table will lose
focus when the mouse moves outside of the frame. Having the slop allows
the user to autoscroll within the table.
******************************************************************************/
Boolean CDragAcrossTable::ShouldScroll( LongPt* mouseLoc, Point* delta )
{
short hDelta = 0;
short vDelta = 0;
short hSpan;
short vSpan;
short hStep = 1;
short vStep = 1;
GetFrameSpan(&hSpan, &vSpan);
GetSteps( &hStep, &vStep ); // TCL 1.1.3 11/30/92 BF
if (mouseLoc->h < frame.left + kAutoScrollSlop ) {
hDelta = Max(-hStep, bounds.left - position.h);
if (hDelta > 0) {
hDelta = 0;
}
} else if (mouseLoc->h > frame.right - kAutoScrollSlop ) {
hDelta = Min(hStep, bounds.right - position.h - hSpan);
if (hDelta < 0) {
hDelta = 0;
}
}
if (mouseLoc->v < frame.top + kAutoScrollSlop ) {
vDelta = Max(-vStep, bounds.top - position.v);
if (vDelta > 0) {
vDelta = 0;
}
} else if (mouseLoc->v > frame.bottom - kAutoScrollSlop ) {
vDelta = Min(vStep, bounds.bottom - position.v - vSpan);
if (vDelta < 0) {
vDelta = 0;
}
}
if ( ( hDelta != 0 ) || ( vDelta != 0 ) )
{
if ( delta != NULL ) // Some callers might not care about the actual delta
SetPt( delta, hDelta, vDelta );
return(TRUE);
}
else
{
return(FALSE);
}
} /* ShouldScroll */
/******************************************************************************
TrackGlobalMouse
Similar to TrackMouse() but all the coordinates are in the global (ie
CDesktop) coordinate system. (Perhaps I should generalize this to be any
view's coordinate system.
******************************************************************************/
void CDragAcrossTable::TrackGlobalMouse( CGlobalMouseTask* aTask, Point startPt )
{
Point currPt;
Point prevPt;
EventRecord macEvent;
currPt = prevPt = startPt;
aTask->BeginTracking( startPt );
while ( StillDown() )
{
gDesktop->Prepare();
GetMouse( &currPt );
aTask->KeepTracking( currPt, prevPt, startPt );
prevPt = currPt;
}
gDesktop->Prepare();
if ( OSEventAvail( mUpMask, &macEvent ) )
{
currPt = macEvent.where;
}
aTask->EndTracking( currPt, prevPt, startPt );
} /* TrackGlobalMouse */
/******************************************************************************
FrameToGlobal
Convert a LongPt from Frame to Global coordinates.
******************************************************************************/
void CDragAcrossTable::FrameToGlobal( LongPt* framePt, Point* globalPt )
{
Point offset;
LongToQDPt( framePt, globalPt);
offset = topLeft((**((WindowPeek)macPort)->contRgn).rgnBBox);
globalPt->v += offset.v - vOrigin;
globalPt->h += offset.h - hOrigin;
} /* FrameToGlobal */